using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Navigation;
using System.Windows.Shapes;
using XnaDevRu.Bullet;
using XnaDevRu.Bullet.Dynamics;
using Microsoft.Xna.Framework;
using Primitive3DSurfaces;

namespace BulletDemo1
{
	/// <summary>
	/// Interaction logic for Page1.xaml
	/// </summary>

	public partial class Page1 : System.Windows.Controls.Page
	{
		private DateTime last = DateTime.Now;
		private Vector3D gravity = new Vector3D( 0, -30, 0 );
		private Random rand = new Random();
		private Dictionary<Brush, GeometryModel3D> models = new Dictionary<Brush, GeometryModel3D>();

		public Page1()
		{
			InitializeComponent();
			CompositionTarget.Rendering += new EventHandler( CompositionTarget_Rendering );

			World.Gravity = gravity;

			AddBox( new Vector3D( 0, -3, 0 ), new Vector3D( 20, 0.5, 20 ), 0 );

//			AddBox( new Vector3D( 0, 0, 0 ), new Vector3D( 0.5, 0.5, 0.5 ), 0 );

//			AddSphere( new Vector3D( 0, 0, 0 ), 0.5, 0 );
//			AddSphere( new Vector3D( 0.2, 2.5, 0.3 ), 0.5, 1 );
//			AddSphere( new Vector3D( 1.2, -1.5, 2 ), 0.5, 0 );
//			AddBox( new Vector3D( 0.2, 4, 0.3 ), new Vector3D( 0.5, 0.5, 0.5 ), 1 );
//			AddBox( new Vector3D( 0.0, 1, 0 ), new Vector3D( 0.5, 0.5, 0.5 ), 0 );

			dropSphere_Click( this, null );

			Trackball trackball = new Trackball();
			trackball.EventSource = captureBorder;
			camera.Transform = trackball.Transform;
			light.Transform = trackball.Transform;

			foreach ( CollisionObject collision in World.CollisionObjects )
			{
				Reset( collision );
			}
		}

		private void Reset( CollisionObject collision )
		{
			RigidBody body = RigidBody.Upcast( collision );

			if ( body != null && body.MotionState != null )
			{
				DefaultMotionState motionState = body.MotionState as DefaultMotionState;
				motionState.GraphicsWorldTransform = motionState.StartWorldTransform;
				collision.WorldTransform = motionState.GraphicsWorldTransform;
				collision.InterpolationWorldTransform = motionState.StartWorldTransform;
				collision.Activate();

				//removed cached contact points
				World.Broadphase.CleanProxyFromPairs( collision.Broadphase );

				if ( body != null && !body.IsStaticObject )
				{
					body.LinearVelocity = new Vector3D( 0, 0, 0 );
					body.AngularVelocity = new Vector3D( 0, 0, 0 );
				}
			}
		}

		private CollisionObject AddBox( Vector3D position, Vector3D halfExtents, double mass )
		{
			BoxShape shape = new BoxShape( halfExtents );
			Microsoft.Xna.Framework.Vector3 inertia = new Vector3D();
			if ( mass != 0 ) shape.CalculateLocalInertia( (float)mass, out inertia );
			Matrix3D trans = new Matrix3D();
			trans.Translate( position );
			Matrix3D center = new Matrix3D();
			MotionState motion = new DefaultMotionState( trans, center );
			RigidBody body = new RigidBody( (float)mass, motion, shape, inertia, 0.5f, 0.5f, 0.5f, 0.5f );

			Brush brush = Brushes.SeaGreen;

			if ( mass == 0 ) brush = Brushes.SandyBrown;
			else
			{
				switch ( rand.Next( 3 ) )
				{
					case 0: brush = Brushes.Red; break;
					case 1: brush = Brushes.Blue; break;
					case 2: brush = Brushes.Green; break;
				}
			}

			body.UserData = new Primitive( "box", brush );

			World.AddRigidBody( body );

			return body;
		}

		private CollisionObject AddSphere( Vector3D position, double radius, double mass )
		{
			SphereShape shape = new SphereShape( (float)radius );
			Microsoft.Xna.Framework.Vector3 inertia = new Vector3D();
			if ( mass != 0 ) shape.CalculateLocalInertia( (float)mass, out inertia );
			Matrix3D trans = new Matrix3D();
			trans.Translate( position );
			Matrix3D center = new Matrix3D();
			MotionState motion = new DefaultMotionState( trans, center );
			RigidBody body = new RigidBody( (float)mass, motion, shape, inertia, 0.5f, 0.5f, 0.5f, 0.5f );
			body.UserData = new Primitive( "sphere", Brushes.Red );

			World.AddRigidBody( body );

			return body;
		}

		private World World
		{
			get { return Resources[ "world" ] as World; }
		}

		/// <summary>
		/// Prepare for rendering
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void CompositionTarget_Rendering( object sender, EventArgs e )
		{
			DateTime now = DateTime.Now;
			World.StepSimulation( (float)( now - last ).TotalMilliseconds );
			last = now;

//			viewport.Children.Clear();

/*			ModelVisual3D lightVisual = new ModelVisual3D();
			lightVisual.Content = new DirectionalLight( Colors.White, new Vector3D( -0.612372, -0.5, -0.612372 ) );
			viewport.Children.Add( lightVisual );
*/
//			List<CollisionObject> delete = new List<CollisionObject>();

			foreach ( CollisionObject collision in World.CollisionObjects )
			{
				if ( collision.IsActive && collision.UserData != null )
				{
					Render( collision );
//					if ( !Render( collision ) ) delete.Add( collision );
				}
			}

/*			foreach ( CollisionObject collision in delete )
			{
				World.RemoveCollisionObject( collision );
			}*/
		}

		private bool Render( CollisionObject collision )
		{
			RigidBody body = RigidBody.Upcast( collision );
			Microsoft.Xna.Framework.Matrix worldMatrix;

			if ( body != null && body.MotionState != null )
			{
				DefaultMotionState motionState = body.MotionState as DefaultMotionState;
				worldMatrix = motionState.GraphicsWorldTransform;
			}
			else
			{
				worldMatrix = collision.WorldTransform;
			}

			Microsoft.Xna.Framework.Matrix displayMatrix = worldMatrix;
			displayMatrix.Translation = Vector3.Zero;
			displayMatrix = Microsoft.Xna.Framework.Matrix.Transpose( displayMatrix );
			displayMatrix.Translation = worldMatrix.Translation;

			Primitive primitive = collision.UserData as Primitive;

			if ( displayMatrix.Translation.Y < -20 )
			{
				if ( primitive.Visual != null ) viewport.Children.Remove( primitive.Visual );

				collision.UserData = null;
				collision.ActivationState = ActivationState.Nothing;
				return false;
			}

			if ( primitive.Visual == null )
			{
				GeometryModel3D model = ( Resources[ primitive.ResourceName ] as ModelVisual3D ).Content as GeometryModel3D;

				if ( model.Material == null )
				{
					if ( !models.ContainsKey( primitive.Brush ) )
					{
						model = model.Clone() as GeometryModel3D;
						MaterialGroup materials = new MaterialGroup();
						materials.Children.Add( new DiffuseMaterial( primitive.Brush ) );
						materials.Children.Add( new SpecularMaterial( primitive.Brush, 50 ) );
						model.Material = materials;
						models[ primitive.Brush ] = model;
					}
					else model = models[ primitive.Brush ];
				}

				primitive.Visual = new ModelVisual3D();
				primitive.Visual.Content = model;

				viewport.Children.Add( primitive.Visual );
			}

			CollisionShape shape = collision.CollisionShape;

			Matrix3D matrix = new Matrix3D();
			if ( shape is BoxShape ) matrix.Scale( ( shape as BoxShape ).HalfExtents * 2 );
			else if ( shape is SphereShape ) matrix.Scale( new Vector3D( ( shape as SphereShape ).Radius, ( shape as SphereShape ).Radius, ( shape as SphereShape ).Radius ) );
			else matrix.Scale( shape.LocalScaling );
			matrix.Append( displayMatrix );

			primitive.Visual.Transform = new MatrixTransform3D( matrix );

			return true;
		}

		protected void dropSphere_Click( object sender, RoutedEventArgs e )
		{
			double radius = 0.5 + ( rand.NextDouble() * 0.5 );
			AddSphere( new Vector3D( ( rand.NextDouble() * 0.2 ) - 0.1, 3, ( rand.NextDouble() * 0.2 ) - 0.1 ), radius, 1 );
		}

		protected void dropBox_Click( object sender, RoutedEventArgs e )
		{
			AddBox(
				new Vector3D( ( rand.NextDouble() * 0.2 ) - 0.1, 3, ( rand.NextDouble() * 0.2 ) - 0.1 ),
				new Vector3D( 0.5 + ( rand.NextDouble() * 0.5 ), 0.5 + ( rand.NextDouble() * 0.5 ), 0.5 + ( rand.NextDouble() * 0.5 ) ),
				2 );
		}
	}
}